package org.byacko.simplemock;

/**
 * 戻り値を設定するインターフェース
 * 
 * @author koumiya
 * 
 * @param <T>
 */
public abstract class ResultBinder<T> {

	private static final Class<?>[] EMPTY_ARG_TYPES = new Class[] {};

	protected MockCreater<T> mockCreater;
	protected String methodName;
	protected Class<?>[] argTypes;

	public ResultBinder(MockCreater<T> mockCreater, String methodName,
			Class<?>[] argTypes) {
		this.mockCreater = mockCreater;
		this.methodName = methodName;
		this.argTypes = argTypes;
	}

	public ResultBinder(MockCreater<T> mockCreater, String methodName) {
		this(mockCreater, methodName, EMPTY_ARG_TYPES);
	}

	/**
	 * スタブの戻り値を設定する。
	 * @param メソッドが呼ばれたときに返る値
	 * @return MockCreater
	 */
	public MockCreater<T> returns(Object value) {
		final MethodHook mh = prepareHook();
		this.mockCreater.bind(mh.setReturn(value));
		return this.mockCreater;
	}

	/**
	 * 発生させるExceptionを設定する。
	 * @param throwable
	 * @return MockCreater
	 */
	public MockCreater<T> raise(Throwable throwable) {
		final MethodHook mh = prepareHook();
		this.mockCreater.bind(mh.setRaise(throwable));
		return this.mockCreater;
	}
	
	/**
	 * メソッド呼び出しを委譲する
	 * @return
	 */
	public MockCreater<T> delegate() {
		return delegate(mockCreater.getDelegateObject());
	}
	
	/**
	 * delegateObjectにメソッド呼び出しを委譲する。
	 * @return
	 */
	public MockCreater<T> delegate(T delegateObject) {
		assertDelegateObject(delegateObject);
		final MethodHook mh = prepareHook();
		this.mockCreater.bind(mh.setDelegate(delegateObject));
		return this.mockCreater;
	}
	
	private void assertDelegateObject(T delegateObject) {
		if(delegateObject == null) {
			throw new IllegalArgumentException("delegateObjectはNullであってはなりません。\n" +
					"引数なしのdelegateメソッドを使用する場合MockCreater#setDelegateObjectから委譲先のオブジェクトを設定してください。");
		}
	}

	protected abstract MethodHook prepareHook();
}
